package meta

import (
	"sync"
	"time"
	"proxy/api"
)

type Backend struct {
	mu         sync.Mutex
	Id         int
	lastRtcId  int
	rtcById    map[int]*Rtc
	expireTime time.Time
	url        string
	connected  bool
}

func NewBackend(id int, url string) *Backend {
	return &Backend{
		Id:         id,
		rtcById:    make(map[int]*Rtc),
		expireTime: time.Now().Add(kBackendTimeout),
		url:        url,
	}
}

func (b *Backend) RtcCount() int {
	b.mu.Lock()
	n := len(b.rtcById)
	b.mu.Unlock()
	return n
}

func (b *Backend) NewRtcId() (RtcId, error) {
	b.mu.Lock()
	b.lastRtcId = (b.lastRtcId + 1) % kRtcIdMax
	id := b.lastRtcId
loop:
	for {
		if _, isDuplicate := b.rtcById[id]; isDuplicate {
			b.lastRtcId = (b.lastRtcId + 1) % kRtcIdMax
			id = b.lastRtcId
		} else {
			break loop
		}
	}

	b.mu.Unlock()
	return RtcId{
		BackendId: b.Id,
		RtcId:     id,
	}, nil
}

func (b *Backend) NewRtc(rtcId int) *Rtc {
	b.mu.Lock()

	newRtc := NewRtc(b.Id, rtcId)

	b.rtcById[rtcId] = newRtc

	b.mu.Unlock()

	return newRtc
}

func (b *Backend) DelRtc(rtcId int) {
	b.mu.Lock()
	delete(b.rtcById, rtcId)
	b.mu.Unlock()
}

func (b *Backend) GetRtc(rtcId int) *Rtc {
	b.mu.Lock()
	rtc := b.rtcById[rtcId]
	b.mu.Unlock()

	return rtc
}

func (b *Backend) SetConnected(connected bool) {
	b.connected = connected
}

func (b *Backend) IsConnected() bool {
	return b.connected
}

func (b *Backend) IsExpired() bool {
	expired := false
	b.mu.Lock()
	expired = b.expireTime.Before(time.Now())
	b.mu.Unlock()
	return expired
}

func (b *Backend) KeepAliveRtcList(rtcIdList []int) {
	now := time.Now()

	b.mu.Lock()

	b.expireTime = now.Add(kBackendTimeout)

	for _, rtcId := range rtcIdList {
		rtc, ok := b.rtcById[rtcId]
		if !ok {
			rtc = NewRtc(b.Id, rtcId)
			b.rtcById[rtcId] = rtc
		}
		rtc.KeepAlive(now)
	}

	b.mu.Unlock()
}

func (b *Backend) Status() *api.BackendStatus {
	b.mu.Lock()

	status := &api.BackendStatus{
		URL:       b.url,
		TTL:       int(b.expireTime.Sub(time.Now()).Seconds()),
		RtcMap:    make(map[int]*api.RtcStatus),
		Connected: b.connected,
	}

	for _, rtc := range b.rtcById {
		rtcStatus := rtc.Status()
		status.RtcMap[rtc.Id.Int()] = rtcStatus
	}
	b.mu.Unlock()

	return status
}

func (b *Backend) HandleRtcTimeout() {

	b.mu.Lock()

	for rtcId, rtc := range b.rtcById {
		if !rtc.IsExpired() {
			continue
		}
		log.Warningf("backend '%d' rtc '%d' expired", b.Id, rtc.Id.Int())
		delete(b.rtcById, rtcId)
	}

	b.mu.Unlock()

	return
}
